﻿using Lib.Data.Enumeration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text.Json.Serialization;

namespace Lib.Data.Entitys; 
/// <summary>
/// 实体类信息
/// </summary>
internal class Entity  {
    /// <summary>
    /// 
    /// </summary>
    public static IDictionary<string, Entity> Cache { get; set; }
    /// <summary>
    /// 
    /// </summary>
    static Entity() {
        Cache = new Dictionary<string, Entity>(StringComparer.OrdinalIgnoreCase);
    }
    /// <summary>
    /// 
    /// </summary>
    public static Entity Create(object e) {
        return Create(e.GetType());
    }
    /// <summary>
    /// 
    /// </summary>
    public static Entity Create(Type Type) {
        if (Type.IsGenericType) {
            var RawType = Nullable.GetUnderlyingType(Type);
            if (RawType != null) {
                Type = RawType;
            }
        }
        var Key = Type.Key();
        if (Cache.TryGetValue(Key, out Entity Value)) {
            return Value;
        }
        return Initialize(Type);
    }
    /// <summary>
    /// 
    /// </summary>
    private static (string, string) Initialize(Entity Re, Type Type) {
        var e = Type.GetCustomAttributes(false).Where(e => e.GetType().Name == "TableAttribute")?.FirstOrDefault();
        var Table = Type.Name; var View = Type.Name;
        if (e != null){
            object Value;
            var GetType = e.GetType();
            foreach (PropertyInfo P in GetType.GetProperties(BindingFlags.Public | BindingFlags.Instance)) {
                Value = P.GetValue(e);
                switch (P.Name) {
                    case "Name":
                        if (Value != null) {
                            Table = Value.ToString();
                            if (Re != null) {
                                Re.Table = Table;
                            }
                        }
                        break;
                    case "View":
                        if (Value != null) {
                            View = Value.ToString();
                            if (Re != null) {
                                Re.View = View;
                            }
                        } else {
                            View = Table;
                        }
                        break;
                    case "Sorts":
                        if (Re != null && Value != null) {
                            Re.Sorts = Value.ToString();
                        }
                        break;
                    case "Description":
                        if (Re != null && Value != null) {
                            Re.Description = Value.ToString();
                        }
                        break;
                }
            }
        }
        if (Re != null) {
            Re.Table = Table; Re.View = View;
        }
        return (Table, View);
    }
    /// <summary>
    /// 
    /// </summary>
    public static Entity Initialize(Type Type) {
        var Re = new Entity {
            Key = Type.Key(),
            Type = Type
        };
        Initialize(Re, Type);
        var BaseType = Type.BaseType;
        while(BaseType != null) {
            BaseType = BaseType.BaseType;
        }
        foreach (PropertyInfo P in Type.GetProperties(BindingFlags.Public | BindingFlags.Instance)) {
            var Column = new Column() {
                Key = P.Name,
                Name = P.Name,
                Type = P.PropertyType,
                That = P
            };
            if(P.GetMethod != null) {
                Column.Virtual = P.GetMethod.IsVirtual;
                if (Column.Virtual) {
                    Column.Name = null;
                }
            }
            Column.Type = P.PropertyType;
            if (Column.Type.IsGenericType) {
                var RawType = Nullable.GetUnderlyingType(Column.Type);
                if (RawType != null) {
                    Column.Type = RawType;
                    Column.IsNullable = true;
                }
            }
            //
            Column.PropertyType = Column.Type.GetPropertyType();
            Column.IsNumber = Column.Type.IsNumber();
            Column.DataType = Column.Type.GetDataType();
            if (Column.IsNullable) {
               
            } else {
                Column.Required = true;
            }
            //
            PropertyInfo Property;
            object Value;
            foreach (object e in P.GetCustomAttributes(false)) {
                var GetType = e.GetType();
                switch (GetType.Name) {
                    case "ColumnAttribute":
                        Property = GetType.GetProperty("Name");
                        if(Property != null) {
                            Value = Property.GetValue(e);
                            if(Value != null) {
                                Column.Name = Value.ToString();
                            }
                        }
                        Property = GetType.GetProperty("Value");
                        if (Property != null) {
                            Column.Value = Property.GetValue(e);
                        }
                        Property = GetType.GetProperty("Size");
                        if (Property != null) {
                            Value = Property.GetValue(e);
                            if (Value != null) {
                                Column.Size = Value.ToInt32();
                            }
                        }
                        Property = GetType.GetProperty("Alias");
                        if (Property != null) {
                            Value = Property.GetValue(e);
                            if (Value != null) {
                                Column.Alias = Value.ToString();
                            }
                        }
                        Property = GetType.GetProperty("Title");
                        if (Property != null) {
                            Value = Property.GetValue(e);
                            if (Value != null) {
                                Column.Title = Value.ToString();
                            }
                        }
                        break;
                    case "DefaultValueAttribute":
                        Property = GetType.GetProperty("Value");
                        if (Property != null) {
                            Value = Property.GetValue(e);
                            if (Value == null) {
                                if (Column.DataType == DataType.DateTime) {
                                    Re.ValueKeys.Add(Column.Key, Column);
                                } else {
                                    Value = Column.Type.IsValueType ? Activator.CreateInstance(Column.Type) : null;
                                }
                            }
                            Column.Value = Value;
                        }
                        break;
                    case "IdentityAttribute":
                        Column.AutoPrimaryKey = true;
                        break;
                  case "IgnoreRenovateAttribute":
                        Column.Renewable = false;
                        break;
                    case "PrimaryKeyAttribute":
                        Column.IsPrimaryKey = true;
                        Property = GetType.GetProperty("Identity");
                        if (Property != null) {
                            Value = Property.GetValue(e);
                            if (Value != null) {
                                Column.AutoPrimaryKey = Value.ToBoolean();
                            }
                        }
                        break;
                    case "StringLengthAttribute":
                        if (Column.DataType == DataType.String) {
                            Property = GetType.GetProperty("MinLength");
                            if (Property != null) {
                                Value = Property.GetValue(e);
                                if (Value != null) {
                                    Column.MinLength = Value.ToInt32();
                                }
                            }
                            Property = GetType.GetProperty("MaxLength");
                            if (Property != null) {
                                Value = Property.GetValue(e);
                                if (Value != null) {
                                    Column.MaxLength = Value.ToInt32();
                                }
                            }
                        }
                        break;
                    case "RequiredAttribute":
                        Column.Required = true;
                        break;
                    case "TimestampAttribute":
                        Column.TimeStamp = true;
                        break;
                    case "SingleAttribute":
                        Property = GetType.GetProperty("Required");
                        if (Property != null) {
                            Value = Property.GetValue(e);
                            if (Value != null) {
                                Column.Required = Value.ToBoolean();
                            }
                        }
                        if (Re.SingleKeys.ContainsKey(Column.Key)) {
                          
                        } else {
                            Re.SingleKeys.Add(Column.Key, Column);
                        }
                        break;
                    case "CombineAttribute":
                        Property = GetType.GetProperty("Required");
                        if (Property != null) {
                            Value = Property.GetValue(e);
                            if (Value != null) {
                                Column.Required = Value.ToBoolean();
                            }
                        }
                        if (Re.CombineKeys.ContainsKey(Column.Key)) {
                           
                        } else {
                            Re.CombineKeys.Add(Column.Key, Column);
                        }
                        break;
                    case "ForeignKeyAttribute":
                        Property = GetType.GetProperty("Key");
                        if (Property != null) {
                            Value = Property.GetValue(e);
                            if (Value != null) {
                                Column.ForeignKey = Value.ToString();
                            } else {
                                Column.ForeignKey = "ID";
                            }
                        }
                        Property = GetType.GetProperty("Type");
                        if (Property != null) {
                            Value = Property.GetValue(e);
                            if (Value != null) {
                                Column.EntityType = Value.ChanageType<Type>();
                            }
                        }
                        Property = GetType.GetProperty("Table");
                        if (Property != null) {
                            Value = Property.GetValue(e);
                            if (Value != null) {
                                Column.Table = Value.ToString();
                            }
                        }
                        if (Column.EntityType != null) {
                            (string Table, string View) = Initialize(null, Column.EntityType);
                            if(Column.Table == null) {
                                Column.Table = Table;
                                Column.View = View;
                            }
                        }
                        break;
                    default:
                        break;
                }
            }
            if (Column.AutoPrimaryKey) {
                Column.Required = false;
                Column.Renewable = false;
            }
            if (Column.IsPrimaryKey) {
                Re.PrimaryKey = Column;
                if (Column.AutoPrimaryKey) {
                    Column.Required = false;
                } else {
                    Column.Required = true;
                    if (Re.SingleKeys.ContainsKey(Column.Key)) {
                        
                    } else {
                        Re.SingleKeys.Add(Column.Key, Column);
                    }
                }
                Column.Renewable = false;
            }
            if(Column.Value != null) {
                Column.Required = false; Re.ValueKeys.Add(Column.Key, Column);
              
            }
            if (Column.Required) {
                Re.Requires.Add(Column.Key, Column);
            }
            if (Column.Name != null) {
                if (Re.Fields.ContainsKey(Column.Name)) {
                   
                } else {
                    Re.Fields.Add(Column.Name, Column);
                }
            }
            if (Column.DataType == DataType.String && (Column.MinLength > 0 || Column.MaxLength > 0)) {
                Re.ConfineKeys.Add(Column.Key, Column);
            }
            Re.Propertys.Add(Column.Key, Column);
        }
        if(Re.CombineKeys.Count == 1) {
            var Pair = Re.CombineKeys.FirstOrDefault();
            Re.SingleKeys.Add(Pair.Key, Pair.Value);
            Re.CombineKeys.Clear();
        }
        if (!Cache.ContainsKey(Re.Key)) {
            Cache.Add(Re.Key, Re);
        }
        return Re;
    }
    /// <summary>
    /// 构造函数
    /// </summary>
    public Entity() {
        this.Fields = new Dictionary<string, Column>(StringComparer.OrdinalIgnoreCase);
        this.Propertys = new Dictionary<string, Column>(StringComparer.OrdinalIgnoreCase);
        this.Requires = new Dictionary<string, Column>(StringComparer.OrdinalIgnoreCase);
        this.CombineKeys = new Dictionary<string, Column>(StringComparer.OrdinalIgnoreCase);
        this.SingleKeys = new Dictionary<string, Column>(StringComparer.OrdinalIgnoreCase);
        this.ValueKeys = new Dictionary<string, Column>(StringComparer.OrdinalIgnoreCase);
        this.ConfineKeys = new Dictionary<string, Column>(StringComparer.OrdinalIgnoreCase);
    }
    /// <summary>
    /// 
    /// </summary>
    [JsonIgnore]
    public Type Type { get; set; }
    /// <summary>
    /// 实体类关键字
    /// </summary>
    public string Key { get; set; }
    /// <summary>
    /// 数据库表名（插入、更新）
    /// </summary>
    public string Table { get; set; }
    /// <summary>
    /// 数据库视图名(查询)
    /// </summary>
    public string View { get; set; }
    /// <summary>
    /// 主键字段
    /// </summary>
    public Column PrimaryKey { get; set; }
    /// <summary>
    /// 数据库表描述
    /// </summary>
    public string Description { get; set; }
    /// <summary>
    /// 默认排序（字段)
    /// </summary>
    public string Sorts { get; set; }
    /// <summary>
    /// 实例化类型, 0.创建对象，1.数据库获取，2.反序列化，3.数据表转化，4.克隆
    /// </summary>
    public EntityType EntityType { get; set; }
    /// <summary>
    /// 字段列表，Key = 字段名
    /// </summary>
    public IDictionary<string, Column> Fields { get; set; }
    /// <summary>
    /// 属性列表，Key = 属性名
    /// </summary>
    public IDictionary<string, Column> Propertys { get; set; }
    /// <summary>
    /// 必填字段（包括非自增主字段），Key = 属性名
    /// </summary>
    public IDictionary<string, Column> Requires { get; set; }
    /// <summary>
    /// 字段值唯一（单字段），Key = 属性名
    /// </summary>
    public IDictionary<string, Column> SingleKeys { get; set; }
    /// <summary>
    /// 字段值唯一（多字段），Key = 属性名
    /// </summary>
    public IDictionary<string, Column> CombineKeys { get; set; }
    /// <summary>
    /// 默认值字段，Key = 属性名
    /// </summary>
    public IDictionary<string, Column> ValueKeys { get; set; }
    /// <summary>
    /// Key = 属性名，Value = Column
    /// </summary>
    public IDictionary<string, Column> ConfineKeys { get; set; }
    /// <summary>
    /// 
    /// </summary>
    public Entity Clone() {
        return (Entity)this.MemberwiseClone();
    }
}
